Skip to content

Cross-compatible hook/middleware + "plugins" which are allowed to be hook and middleware#1284

Merged
brandur merged 14 commits into
masterfrom
brandur-plugins
Jul 13, 2026
Merged

Cross-compatible hook/middleware + "plugins" which are allowed to be hook and middleware#1284
brandur merged 14 commits into
masterfrom
brandur-plugins

Conversation

@brandur

@brandur brandur commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

This one's largely aimed at extending a few parts of otelriver to be able to
emit some additional useful metrics like time that it takes to lock jobs,
number of jobs locked per batch, or any other arbitrary metrics we want to emit
down the road. I previously had something similar back in #1203, but here we
extract an isolated piece of it.

Introduce Config.Plugins as the preferred place to install global extensions.
A plugin may implement hooks, middleware, or both, allowing integrations to
participate in multiple lifecycle phases without being split across
configuration fields.

Config.Hooks and Config.Middleware remain supported but are deprecated.
Hooks that also implement middleware, and middleware that also implement hooks,
now activate both sides automatically.

Internally, replace the separate hook and middleware lookup paths with a
unified plugin lookup used by insertion, job execution, periodic jobs, and
rivertest. Existing defaults and BaseService initialization continue to
work through the unified path.

Hooks/middleware duplicated across multiple properties (Config.Hooks,
Config.Middleware, Config.Plugins) are deduplicated, so some effort should
be made to make sure any particular plugin is in only one place. For
simplicity, it's easiest just to put everything in Config.Plugins.

@bgentry bgentry left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs changelog + homepage docs, but lgtm! 🚀

@brandur brandur force-pushed the brandur-plugins branch from c991820 to e59702f Compare July 6, 2026 20:00
brandur added 2 commits July 8, 2026 14:53
…hook and middleware

This one's largely aimed at extending a few parts of `otelriver` to be
able to emit some additional useful metrics like time that it takes to
lock jobs, number of jobs locked per batch, or any other arbitrary
metrics we want to emit down the road. I previously had something
similar back in #1203, but here we extract an isolated piece of it.

This change does two things:

* If either a hook sent to `Config.Hooks` implements a middleware or a
  middleware sent to `Config.Middleware` implements a hook, activate its
  alternate side as well.

* Establish a new `Config.Plugins` that acts as a more generalized place
  where a hook/middleware can go. We define a plugin as this type:

        type Plugin interface {
            Hook
            Middleware
        }

The reason for the first point is better backward compatibility.
Notably, if I add a hook to `otelriver.Middleware`, I want it to be able
to still work even if the user doesn't explicitly movie it from
`Config.Middleware` to `Config.Plugins`.
Make hooks and middleware opt into `Plugin` through their default embedders
and function helpers. Keep `Config.Plugins` as the unified registration path.
@brandur brandur force-pushed the brandur-plugins branch from e59702f to f8d3c2a Compare July 8, 2026 19:53
Merge the split lookup packages into one precomputed plugin lookup.
Build the kind buckets up front and use the unified path throughout
client startup and job execution.
@brandur brandur force-pushed the brandur-plugins branch from f8d3c2a to 0e83736 Compare July 8, 2026 19:57
@brandur

brandur commented Jul 8, 2026

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0e83736133

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread internal/pluginlookup/plugin_lookup.go Outdated
Comment on lines +94 to +101
for _, hook := range hooks {
if plugin, ok := hook.(rivertype.Plugin); ok {
normalizedPlugins = append(normalizedPlugins, plugin)
}
}
for _, middleware := range middlewares {
if plugin, ok := middleware.(rivertype.Plugin); ok {
normalizedPlugins = append(normalizedPlugins, plugin)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve non-plugin hook registrations

NormalizePlugins now only carries registrations forward when they also satisfy rivertype.Plugin, but the public Config.Hooks, Config.Middleware, and JobArgsWithHooks APIs still accept plain rivertype.Hook/rivertype.Middleware values. In applications with custom hooks or middleware that manually implement IsHook/IsMiddleware (or embed both defaults without adding an unambiguous IsPlugin), those entries are silently dropped from every lookup, so their insert/work behavior stops running after this change; the original hook/middleware side should still be preserved even when the value is not a plugin.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed this one up.

Comment thread rivertest/worker.go Outdated
JobRow: job,
MiddlewareLookupGlobal: middlewarelookup.NewMiddlewareLookup(append(rivermiddleware.DefaultMiddleware(), w.config.Middleware...)),
PluginLookupByJob: pluginlookup.NewJobPluginLookup(),
PluginLookupGlobal: pluginlookup.NewPluginLookup(pluginlookup.NormalizePlugins(effectiveHooks, append(rivermiddleware.DefaultMiddleware(), effectiveMiddleware...), nil)),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid double-registering cross-compatible test plugins

In rivertest.Worker, effectiveHooks and effectiveMiddleware already expand cross-compatible registrations and plugins into both slices, but this line then normalizes both slices as independent plugin sources. When a test config uses a Config.Hooks item that also implements middleware, a Config.Middleware item that also implements hooks, or a Config.Plugins item implementing both, the same value is added twice to the plugin lookup and its hook/middleware runs twice under WorkJob, diverging from real Client behavior.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Followed this rabbit hole down, but trying to get into deduplication introduces a lot of edge cases (e.g. can't distinguish between zero-size pointers which are different structs, would have to try to dedup struct values in addition to struct pointers which gets questionable), so think it's best here to just require users only put any particular plugin in one place only.

brandur added 3 commits July 12, 2026 16:07
It turns out that this is really hard to do in a way that's completely
reliable. Even if we detect duplicate pointers, there's still room for a
user to have used a duplicate struct. We also can't detect duplicate for
zero-length structs. Since this is an edge case of an edge case, let's
remove all plugin dedup/validation. If a user does this, they'll just
have to live with the consequences.
@brandur

brandur commented Jul 13, 2026

Copy link
Copy Markdown
Contributor Author

Did a little rework on this one so that any existing hooks/middleware can be immediately moved to the new plugins section with no changes needed, and reworked the internal lookup paths so we unify hooks/middleware together (hooklookup and middlewarelookup just become pluginlookup). We're at our Codex review quota still I think, so I asked Codex to do the reviews locally instead. It found a couple things, but they were all related to possible plugin duplication, and pulling those threads I found there's no great way to handle all the possible edge cases there, so I ended up reverting some of those fixes in favor of requiring users not duplicate plugins between sections (and if they do, they'll just have to deal with the consequences, but this shouldn't be particularly common).

(Oh and added changelog.)

@brandur brandur merged commit 5f1913a into master Jul 13, 2026
15 checks passed
@brandur brandur deleted the brandur-plugins branch July 13, 2026 18:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants